#ifndef XG_HTTPBASE_H
#define XG_HTTPBASE_H
////////////////////////////////////////////////////////
#include "../stdx/all.h"
#include "../dbx/DBConnectPool.h"

#define CGI_PUBLIC		3
#define CGI_PROTECT 	2
#define CGI_PRIVATE 	1
#define CGI_DISABLE 	0

#define HTTP_CHARSET				"UTF-8"
#define HTTP_SPACELIST				"/\\ \r\n\t"

#ifndef HTTP_REQHDR_MAXSIZE
#define HTTP_REQHDR_MAXSIZE			8 * 1024
#endif

#ifndef HTTP_SENDBUF_MAXSIZE
#define HTTP_SENDBUF_MAXSIZE 		128 * 1024
#endif

#ifndef HTTP_REQDATA_MAXSIZE
#define HTTP_REQDATA_MAXSIZE		8 * 1024 * 1024
#endif

#ifndef HTTP_RESDATA_MAXSIZE
#define HTTP_RESDATA_MAXSIZE		8 * 1024 * 1024
#endif

#ifndef HTTP_REQUEST_MAXTIMES
#define HTTP_REQUEST_MAXTIMES		1000
#endif

class HttpRequest;
class HttpResponse;
class IHttpRequest;
class IHttpResponse;
class HttpProcessBase;

typedef const char* (*GET_HTTP_CGI_PATH_FUNC)();
typedef HttpProcessBase* (*CREATE_HTTP_CGI_FUNC)();
typedef void(*DESTROY_HTTP_CGI_FUNC)(HttpProcessBase* obj);

class CgiMapData : public Object
{
public:
	const static int URL_FLAG = 0;
	const static int CGI_FLAG = 1;
	const static int IDX_FLAG = 2;
	const static int NONE_CODE = 0;
	const static int GZIP_CODE = 1;

	static const CgiMapData& NullObject();
	static string GetKey(const string& url);

public:
	int maxsz;
	int maxcnt;
	int access;
	int hostmaxcnt;

public:
	int flag;
	int code;
	string url;
	string dest;
	string param;
	string extdata;
	sp<DllFile> dll;
	CREATE_HTTP_CGI_FUNC create_cgi;
	DESTROY_HTTP_CGI_FUNC destroy_cgi;

	CgiMapData() : flag(URL_FLAG), code(NONE_CODE), maxcnt(0), maxsz(HTTP_REQDATA_MAXSIZE), access(CGI_PRIVATE), hostmaxcnt(0), create_cgi(NULL), destroy_cgi(NULL){}
	CgiMapData(int _flag, int _code, const string& _url) : flag(_flag), code(_code), url(_url), maxcnt(0), maxsz(HTTP_REQDATA_MAXSIZE), access(CGI_PRIVATE), hostmaxcnt(0), create_cgi(NULL), destroy_cgi(NULL){}
};

class HttpSession : public Session
{
	friend class HttpServer;

private:
	time_t etime;
	time_t ctime;
	TSMap<string, string> datamap;

	HttpSession(const HttpSession& session);

	HttpSession(const string& name, int timeout)
	{
		this->name = name;
		this->ctime = time(NULL);
		this->setTimeout(timeout);
	}

public:
	bool clear();
	bool disable();
	int size() const;
	long getTimeout() const;
	bool setTimeout(long second);
	bool remove(const string& key);
	bool set(const map<string, string>& attrmap);
	bool set(const string& key, const string& val);
	bool get(const string& key, string& val) const;

	bool isTimeout() const
	{
		return etime <= time(NULL);
	}
	time_t getCreateTime() const
	{
		return ctime;
	}
	bool isTimeout(time_t now) const
	{
		return etime <= now;
	}
	string get(const string& key) const
	{
		return Session::get(key);
	}
};

class IHttpServer : public Object
{
protected:
	int id = 0;
	int timeout = 60;
	int reqmaxsz = HTTP_REQDATA_MAXSIZE;

public:
	virtual int getPort() = 0;
	virtual string getName() = 0;
	virtual string getHost() = 0;
	virtual string getPath() = 0;
	virtual string getSequence() = 0;
	virtual bool updateCgiData(const string& url) = 0;
	virtual void removeSession(const string& name) = 0;
	virtual void removeCgiAccess(const string& url) = 0;
	virtual int getCgiAccess(const string& url) const = 0;
	virtual CgiMapData getCgiMapData(const string& url) = 0;
	virtual string getCgiExtdata(const string& url) const = 0;
	virtual int getCgiMap(map<string, CgiMapData>& cgimap) = 0;
	virtual void setCgiAccess(const string& url, int access) = 0;
	virtual sp<Session> getSession(const string& name, int timeout = 0) = 0;
	virtual void setCgiExtdata(const string& url, const string& extdata) = 0;
	virtual bool updateModuleFile(const string& src, const string& dest) = 0;

	virtual sp<DBConnect> getDBConnect() = 0;
	virtual void disableDBConnect(sp<DBConnect> conn) = 0;
	virtual string getMimeType(const string& key) const = 0;
	virtual int getFileContent(const string& path, SmartBuffer& content) = 0;
	virtual map<string, tuple<string, string, string>> getCgiDocMap() const = 0;
	virtual tuple<string, string, string> getCgiDoc(const string& url) const = 0;
	virtual void setCgiDoc(const string& url, const string& reqdoc, const string& rspdoc, const string& remark) = 0;

	int getId() const
	{
		return id;
	}
	bool isActive() const
	{
		return timeout > 0;
	}
	int getTimeout() const
	{
		return timeout;
	}
};

class IHttpRequest : public Object
{
	friend class HttpHelper;

protected:
	int hdrsz;
	int datsz;
	int addsz;
	string path;
	string param;
	string payload;
	string version;
	string boundary;
	CgiMapData cgidata;
	SmartBuffer content;
	E_HTTP_METHOD method;

	void setMethod(E_HTTP_METHOD method)
	{
		this->method = method;
	}

public:
	virtual bool isMobile() const = 0;
	virtual SmartBuffer toBuffer() const = 0;
	virtual string getHeadString() const = 0;
	virtual string getDataString() const = 0;
	virtual bool setCookie(const string& val) = 0;
	virtual string getCookie(const string& key) const = 0;
	virtual string getDataValue(const string& key) const = 0;
	virtual string getHeadValue(const string& key) const = 0;
	virtual int getParameterKeys(vector<string>& vec) const = 0;

	int getHeadSize() const
	{
		return hdrsz;
	}
	int getDataSize() const
	{
		return datsz;
	}
	int getAdditionSize() const
	{
		return addsz;
	}
	const string& getPath() const
	{
		return path;
	}
	SmartBuffer getContent() const
	{
		return content;
	}
	E_HTTP_METHOD getMethod() const
	{
		return method;
	}
	const string& getVersion() const
	{
		return version;
	}
	const string& getBoundary() const
	{
		return boundary;
	}
	const string& getPayload() const
	{
		return payload;
	}
	void setPayload(const string& val)
	{
		payload = val;
	}
	const string& getParamString() const
	{
		return param;
	}
	const CgiMapData& getCgiData() const
	{
		return cgidata;
	}
	string getParameter(const string& key) const
	{
		return getDataValue(key);
	}
	IHttpRequest() : method(eUNKNOWN), hdrsz(0), datsz(0), addsz(0), version("HTTP/1.1")
	{
	}
};

class IHttpResponse : public Object
{
protected:
	int status = 200;

public:
	int getStatus() const
	{
		return status;
	}
	void setStatus(int status)
	{
		this->status = status;
	}

public:
	virtual string getSocketHost() const = 0;
	virtual string getClientHost() const = 0;
	virtual sp<Socket> getSocket() const = 0;
	virtual string getHeadString() const = 0;
	virtual string getContentType() const = 0;
	virtual bool setContentType(const string& val) = 0;
	virtual string getHeadValue(const string& key) const = 0;
	virtual bool setHeadValue(const string& key, const string& val) = 0;
	virtual void addCookie(const string& key, const string& val, int timeout = 0) = 0;
};

class HttpHeadNode : public ContentNode
{
public:
	string toString() const;
	bool parse(const string& msg, bool inited = true);
};

class HttpDataNode : public ContentNode
{
protected:
	string& operator [] (const string& key);

public:
	HttpDataNode()
	{
		setKeySpliter("=");
		setEndSpliter("&");
	}
	string getValue(const string& key) const
	{
		return stdx::DecodeURL(ContentNode::getValue(key));
	}
	bool setValue(const string& key, const string& val)
	{
		return ContentNode::setValue(key, stdx::EncodeURL(val));
	}
	template<class DATA_TYPE> bool setValue(const string& key, const DATA_TYPE& val)
	{
		return ContentNode::setValue(key, stdx::EncodeURL(stdx::str(val)));
	}
};

class HttpProcessBase : public Object
{
	friend class ProcessBase;

protected:
	sp<MemFile> file;
	StringCreator out;

	int clearResponse()
	{
		out.clear();
		file = NULL;

		return XG_OK;
	}
	bool createFile(int maxsz)
	{
		sp<MemFile> file = newsp<MemFile>();

		CHECK_FALSE_RETURN(file->create(maxsz));

		this->file = file;

		return true;
	}

public:
	const MemFile* getOutFile() const
	{
		return file.get();
	}
	const string& getOutString() const
	{
		return out.getContent();
	}
	virtual int doWork(HttpRequest* request, HttpResponse* response)
	{
		return 0;
	}
};
////////////////////////////////////////////////////////
#endif
